vlwkaos' digital garden

Rust - CLI Nicer error reporting

.unwrap() 은 Result값을 match 하여 Error일 때 알아서 panic! 매크로를 실행해주는 단축어다.

이전의 코드를 이렇게 한줄로 줄일 수 있다.

let f = BufReader::new(File::open(&args.path).unwrap());

에러 자체를 return하게 하고 싶을 때는 ?을 이용할 수 있다. 이 경우 main 함수의 리턴값이 바뀌므로 함수의 시그니처를 변경해줘야한다

fn main() -> Result<(), Box<dyn std::error::Error>> {
    //
    //... 코드 중간에 Error가 반환될 수 있다.
    //
    Ok(()) // 여전히 종료는 Unit type을 반환한다
}

Box<dyn std::error::Error 는 모든 에러를 담을 수 있는 타입이다.

좀 더 커스텀해보기

#[derive(Debug)]
struct CustomError(String);

fn main() -> Result<(), CustomError> {
    let content = std::fs::read_to_string(path)
        .map_err(|err| CustomError(format!("Error reading `{}`: {}", path, err)))?;
    println!("file content: {}", content);
    Ok(())
}

output:

Error: CustomError("Error reading `test.txt`: No such file or directory (os error 2)")

이런 형태의 에러처리는 자주 쓰인다. 다만 에러 자체를 저장하기보다 CustomError 안의 문자열만 알게 되는 문제가 있다.

이때는 anyhow 라는 라이브러리를 사용한다. Context trait을 활용하여 에러에 대한 추가 메세지를 작성해주면서 원래의 에러까지 유지할 수 있다.

장점은? 에러에 대한 체이닝이 가능해진다.

use anyhow::{Context, Result};

fn main() -> Result<()> {
    let path = "test.txt";
    let content = std::fs::read_to_string(path)
        .with_context(|| format!("could not read file `{}`", path))?;
    println!("file content: {}", content);
    Ok(())
}

output:

Error: could not read file `test.txt`

Caused by:
    No such file or directory (os error 2)

[[Rust - CLI Output]]

Rust - CLI Nicer error reporting